<?php

// Prevent much happening if this script is called directly
if ( !defined('INCS') ) exit;

/*

    Form Class

    Implements the back-end for a form.  Validates and Sanitises Form Input.

    Methods:
        o getInstance( void ) - @return form instance
        o getNewForm_as_string( void ) - @return string markup of an empty form.
        o getForm_as_string( void )

    CHANGELOG
        o 2009-04-20T22:49:00 +01:00 - Created.

*/




class Form
{

    // Singleton Class - cannot be cloned and can only be instantiated
    // through getInstance().
    private function __clone(){}
    private function __construct(){}
    public static function getInstance( $config = '' )
    {
        if (!self::$m_pInstance)
        {
            self::$m_pInstance = new Form();

            require_once( INCS . 'html_tags.inc.php' );
            require_once( INCS . 'form_element.inc.php' );
            require_once( INCS . 'sanitisation.inc.php' );
            require_once( INCS . 'validation.inc.php' );

            self::$m_pInstance->load_controls( $config );
        }

        return self::$m_pInstance;
    }



    // Private Properties
    private static $m_pInstance;
    private static $validates;


    // Public Properties
    public $clean_token;
    public $controls;

    public $recipient;
    public $visitor_name;
    public $visitor_email;
    public $visitor_message;

    public $has_errors;



    // Private Methods

    // Loads control object(s) using config arrays to set various properties of
    // those control(s).
    // FormElement children need to be registered as cases to the switch
    // statement herein.
    // @param array of arrays containing configuration details for each control.
    // @return void.
    private function load_controls( $config )
    {
        foreach( $config as $name => $elem_cfg )
        {
            switch( $elem_cfg['tagname'] )
            {
                case 'input':
                    if ( array_key_exists('type', $elem_cfg) && $elem_cfg['type'] == 'hidden')
                    {
                        self::$m_pInstance->controls[] = new HiddenElement( $name, $elem_cfg );
                        break;
                    }
                    // other input types fall through to the next case.
                case 'textarea':
                    self::$m_pInstance->controls[] = new InputElement( $name, $elem_cfg );
                    break;
                case 'select':
                    self::$m_pInstance->controls[] = new SelectorElement( $name, $elem_cfg );
                    break;
                default:
                    trigger_error( get_log_entry("Cannot find a control object defined for " .
                        (isset($elem_cfg['tagname']) ?
                        "a tag named {$elem_cfg['tagname']}." :
                        "a missing tagname for $name form object.")), E_USER_WARNING );
                    display_nice_error();
            }
        }
    }


    private function acceptInputs()
    {
        foreach( self::$m_pInstance->controls as $c )
        {
            switch($c->control_name)
            {
                case 'Name':
                    self::$m_pInstance->visitor_name = $c->value;
                    break;
                case 'Email':
                    self::$m_pInstance->visitor_email = $c->value;
                    break;
                case 'Comment':
                    self::$m_pInstance->visitor_message = $c->value;
                    break;
            }
        }
    }


    public function set_form_token( $ref_dirty )
    {
        // Token must be a string obviously
        if ( !is_string( $ref_dirty ) ) return 0;
        // Token must be 40 chars
        if ( mb_strlen( $ref_dirty ) != 40 ) return 0;
        // Token must be hex chars
        if( !mb_ereg("^[0-9a-zA-Z]+$", $ref_dirty)) return 0;

        // OK it passed
        self::$m_pInstance->clean_token = $ref_dirty;
        return 1;
    }


    // Handle Input from form
    // @param void.
    // @return integer number of validation errors encountered.
    public function do_submission()
    {
        $err_count = 0;

        foreach( self::$m_pInstance->controls as $c )
        {
            // Get POST data.
            $unsafe = $_POST[$c->control_name];

            // trim the input if it is a string.
            if ( is_string($unsafe) ) $unsafe = trim( $unsafe );

            // Call FormElement::Validator() with the Input to be validated.
            $err_count += $c->validator( $unsafe );
        }

        if ($err_count == 0)
        {
            self::$m_pInstance->acceptInputs();

            require_once( INCS . 'mail.inc.php' );
            global $email_config;
            $e = Mail::getInstance( $email_config );
            if ( !$e->send( &$this ) )
            {
                trigger_error( 'Problem sending in email->send().  see secure logfile to see if message can be retrieved.', E_USER_WARNING);
                $d = date("c");
                $todays_log = FORM_PARENT_PATH . '/lzu6/' . date("Y-m-d") . '_form_subs.log';
                error_log( "\n" . get_log_entry("$d Send Mail failed for this message."), 3, $todays_log );
                display_nice_error();
            }
        }

        self::$m_pInstance->has_errors = $err_count;
        return $err_count;

    }


    // Returns a string representing the form elements.
    // If Form::has_errors is 0 or NULL then the elements will be in a state of
    // readiness for input, otherwise they may contain previously input,
    // escaped values and may have error highlighting set.
    // @param void.
    // @return string of form element markup for output.
    public function displayForm( $form_config )
    {
        // Form Element
        $f = new HTML_Element;
        $f->tagname = 'form';
        $f->attributes = "action=\"{$form_config['form_action']}\"";
        $f->attributes .= " method=\"{$form_config['form_method']}\"";
        $f->attributes .= " {$form_config['form_attributes']}";

        // Get the markup for each form element
        $form_controls_markup = '';

        foreach( self::$m_pInstance->controls as $c )
        {
            $form_controls_markup .= $c->getMarkup();
        }

        // Wrapper around controls
        $cw;
        if ( isset($form_config['form_controls_wrapper_tagname']) && !empty($form_config['form_controls_wrapper_tagname']) )
        {
            $cw = new HTML_Element;
            $cw->tagname = $form_config['form_controls_wrapper_tagname'];
            if ( isset($form_config['form_controls_wrapper_attributes']) &&
                !empty($form_config['form_controls_wrapper_attributes']) )
                $cw->attributes = $form_config['form_controls_wrapper_attributes'];
            $cw->innerValue = $form_controls_markup;
            $form_controls_markup = $cw->getString();
        }

        // Submission Control
        $submit = new HTML_Element;
        $submit->tagname = $form_config['form_submission_method']['tagname'];
        $submit->attributes = "name=\"{$form_config['form_submission_method']['name']}\"";
        $submit->attributes .= " type=\"{$form_config['form_submission_method']['type']}\"";
        $submit->attributes .= " value=\"{$form_config['form_submission_method']['value']}\"";
        $submit->attributes .= " {$form_config['form_submission_method']['attributes']}";

        // Fieldset
        $fieldset;
        if ( isset($form_config['form_legend']) && !empty($form_config['form_legend']) )
        {
            $legend = new HTML_Element;
            $legend->tagname = 'legend';
            if ( isset($form_config['form_legend_attributes']) &&
                !empty($form_config['form_legend_attributes']) )
                $legend->attributes = $form_config['form_legend_attributes'];
            $legend->innerValue = $form_config['form_legend'];

            $fieldset = new HTML_Element;
            $fieldset->tagname = 'fieldset';
            $fieldset->innerValue = $legend->getString() . $form_controls_markup . $submit->getString();

            $f->innerValue = $fieldset->getString();
        }
        else
        {
            $f->innerValue = $form_controls_markup . $submit->getString();
        }


        return $f->getString();
    }

}
